package com.greatwqs.wmall.common.provider.exception;

import com.greatwqs.wmall.common.module.exception.AppException;
import com.greatwqs.wmall.common.module.exception.ErrorCode;
import com.greatwqs.wmall.common.module.model.HttpResponse;

import lombok.extern.slf4j.Slf4j;

import org.apache.skywalking.apm.toolkit.trace.TraceContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

import java.util.List;
import java.util.Locale;

/**
 * 全局异常处理
 *
 * @author wang-qingsong
 */
@Slf4j
@ControllerAdvice
public class AppExceptionHandler {

    @Autowired
    private MessageSource messageSource;

    @ResponseBody
    @ExceptionHandler(AppException.class)
    public ResponseEntity<HttpResponse> appException(AppException appException) {
        final ErrorCode errorCode = appException.getErrorCode();
        log.warn("AppExceptionHandler, appException, errorCode: {}", errorCode, appException);

        Locale locale = LocaleContextHolder.getLocale();
        String message = messageSource.getMessage(errorCode.getI18nKey(), null, locale);
        Integer appCode = errorCode.getAppCode();
        return ResponseEntity.status(errorCode.getHttpStatus())
                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .body(HttpResponse.of(TraceContext.traceId(), appCode, message, appException.getData()));
    }

    @ResponseBody
    @ExceptionHandler(Exception.class)
    public ResponseEntity<HttpResponse> exception(Exception exception) {
        final ErrorCode errorCode = ErrorCode.INTERNAL_SERVER_ERROR;
        log.error("AppExceptionHandler, exception: {}", exception.getClass().getName(), exception);

        Locale locale = LocaleContextHolder.getLocale();
        String message = messageSource.getMessage(errorCode.getI18nKey(), null, locale);
        Integer appCode = errorCode.getAppCode();
        return ResponseEntity.status(errorCode.getHttpStatus())
                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .body(HttpResponse.of(TraceContext.traceId(), appCode, message));
    }

    @ResponseBody
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public HttpResponse methodArgumentNotValidException(MethodArgumentNotValidException exp) {
        final ErrorCode errorCode = ErrorCode.BAD_REQUEST;
        BindingResult bindingResult = exp.getBindingResult();
        List<FieldError> fieldErrorList = bindingResult.getFieldErrors();
        if (!CollectionUtils.isEmpty(fieldErrorList)) {
            for (FieldError fieldError : fieldErrorList) {
                // error notes one by one!
                if (fieldError != null) {
                    log.info("methodArgumentNotValidException, field: {}, error: {}", fieldError.getField(), fieldError.getDefaultMessage());
                    return HttpResponse.of(TraceContext.traceId(), errorCode.getAppCode(), fieldError.getDefaultMessage());
                }
            }
        }

        log.warn("methodArgumentNotValidException, Please Check exp", exp);
        Locale locale = LocaleContextHolder.getLocale();
        String message = messageSource.getMessage(errorCode.getI18nKey(), null, locale);
        return HttpResponse.of(TraceContext.traceId(), errorCode.getAppCode(), message);
    }
}
